package com.example.springaop.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 23735
 * Date: 2023-03-11
 * Time: 22:35
 */

// 定义切面类
@Aspect // 表示这个类是一个切面类
@Component // 注意不用忘了添加到 spring boot 中, 因为 与那些类一样 启动过项目之后就需要使用
public class UserAspect {

//    定义切点, 函数里面不需要具体的实现, 因为主要就是利用一下函数名
    // 连接点(触发 aop )是 返回值任意  的哪个包下 的UserController 中的参数任意的 sayhi 方法
//    其中的 权限修饰符, 以及异常省略了
//    这里使用的是 AspectJ(与Spring AOP一样的效果, 只不过是民间的但比 spring aop 好用) 语法
//    spring aop 没办法就 像 AspectJ 靠拢, 兼容它
    @Pointcut("execution(* com.example.springaop.controller.UserController.sayhi(..))")
    public void pointcut1(){}

    @Pointcut("execution(* com.example.springaop.controller.UserController.sayhello(..))")
    public void pointcut2(){}

//    定义通知, 就是具体方法的实现


//    前置通知
//    针对 pointcut() 这个 advice 的具体实现
    @Before("pointcut1()")
    public void doBefore(){
        System.out.println("前置通知");
    }

//    抛异常后通知
    @AfterThrowing("pointcut1()")
    public void doAfterThrow(){
        System.out.println("抛异常后通知");
    }


//    返回后通知(如果执行, 那么在后置通知之前执行)
    @AfterReturning("pointcut1()")
    public void doAfterReturn(){
        System.out.println("返回后通知");
    }


    //    后置通知 (不管是否触发异常都会执行)
    @After("pointcut1()")
    public void doAfter(){
        System.out.println("后置通知");
    }


    //    环绕通知
    @Around("pointcut1()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        Object result=null;
//        around 方法会包裹所有方法的执行
//        即这个会在 前置方法前打印
        System.out.println("around 方法开始执行");
        try {
//            执行目标方法以及这个方法对应的通知
            result=joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
//        即这个会在 后置方法后打印
        System.out.println("around 方法执行结束");
        return result;
    }

    //    基于环绕通知实现对一个方法的执行时间的计算
    @Around("pointcut2()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object result=null;
        StopWatch stopWatch=new StopWatch();
        try {
            stopWatch.start();
//            执行目标方法以及这个方法对应的通知
            result=joinPoint.proceed();
            stopWatch.stop();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        System.out.println(joinPoint.getSignature().getDeclaringType()+"."
                + joinPoint.getSignature().getName()+"()"+"执行的时间是"
                +stopWatch.getTotalTimeMillis()+"ms");
        return result;
    }


}
